package com.kj.tms.common.utils.calx;


import com.kj.tms.common.utils.StringUtil;
import com.kj.tms.common.utils.calx.sheet.Sheet;
import com.kj.tms.common.utils.calx.util.Utility;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ProjectName: ledger
 * @Package: com.hongtu.tms.ledger.calx.factory.cell
 * @ClassName: Cell
 * @Description: []
 * @Author: [zhuyongjie]
 * @CreateDate: 2018/4/18 18:23
 * @UpdateUser: []
 * @UpdateDate: 2018/4/18 18:23
 * @UpdateRemark: []
 * @Version: 1.0
 * Copyright: 北京宏图天安 Copyright (c) 2018
 */
public class Cell {
    private Logger logger = LoggerFactory.getLogger(getClass());
    /**
     * 单元格data-cell对象值
     */
    private String dataCell;
    /**
     * 单元格计算公式;
     * data-formula对应值
     */
    private String dataFormula;
    /**
     * 单元格格式化类型
     * data-format对应值
     */
    private String dataFormat;
    /**
     * 单元格所在sheet页
     */
    private Sheet sheet;
    /**
     * 单元格对应的值
     */
    private String value;
    /**
     * 格式化后的值
     */
    private String formattedValue;
    /**
     * 计算后的值
     */
    private String computedValue;
    /**
     * 浮点值
     */
    private String floatValue;
    /**
     * 是否受其他单元格影响,存在依赖
     */
    private boolean affected = false;
    /**
     * 是否处理完成
     */
    private boolean processed = false;
    /**
     * 包含依赖于此单元格的表的注册表
     */
    private Map<String,Cell> dependant;

    /**
     *包含此单元格依赖的的注册表:<单元格地址，单元格对象>
     */
    private Map<String,Cell> dependencies;
    /**
     * 单元格地址,同dataCell值
     */
    private String address;
    /**
     * 是否有其他sheet页依赖
     */
    private boolean remoteDependency = false;
    /**
     * 单元格对应字段名称,比如AMTA_2对应AMTA
     */
    private String fieldName;
    /**
     * 台账单元格行标识，比如AMTA_2对应2
     */
    private String rowNo;

    /**
     * @Method      Cell
     * @Param		dataCell
     * @Param		dataFormula
     * @Param		dataFormat
     * @Param		sheet
     * @Param		value
     * @Return
     * @Exception
     * @Description []
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date
     */
    public Cell(String dataCell, String dataFormula,
                String dataFormat, Sheet sheet,
                String value, String fieldName, String rowNo) {
        this.dataCell = dataCell.toUpperCase();
        this.dataFormula = dataFormula;
        this.dataFormat = dataFormat;
        this.sheet = sheet;
        this.value = value;
        this.fieldName = fieldName;
        this.rowNo = rowNo;
        this.init();
    }

    /**
     * @Method      Cell
     * @Param
     * @Return
     * @Exception
     * @Description [空构造函数]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date
     */
    public Cell() {
        this.init();
    }

    /**
     * @Method      init
     * @Param
     * @Return      void
     * @Exception
     * @Description [单元格初始化工作]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 11:37
     */
    public void init(){
//        System.out.println("cell.init() : 单元格 [" + this.dataCell + "] 正在初始化...");
//        logger.info("cell.init() : 单元格 [" + this.dataCell + "] 正在初始化...");
        if(!StringUtil.isEmptyOrNull(this.dataCell)){
            this.address = this.dataCell.toUpperCase();
        }
        dependencies = new HashMap<String,Cell>();
        dependant = new HashMap<String,Cell>();
        if(!StringUtil.isEmptyOrNull(this.dataFormula)){
            this.dataFormula = this.dataFormula.toUpperCase();
        }
    }//cell.init() end

    /**
     * @Method      buildDependency
     * @Param
     * @Return      void
     * @Exception
     * @Description [建立单元格依赖关系，用于触发计算]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 16:10
     */
    public void buildDependency() throws Exception {
      /* String[] patten = {
               //remoteCellRange表间单元格范围
               "/[A-Za-z0-9_]+\\s*!\\s*[A-Za-z]+[0-9]+\\s*:\\s*[A-Za-z]+[0-9]+/g",
               //remoteCell表间单元格
               "/[A-Za-z0-9_]+\\s*!\\s*[A-Za-z]+[0-9]+/g",
               //cellRange单元格范围
               "/[A-Za-z]+[0-9]+\\s*:\\s*[A-Za-z]+[0-9]+/g",
               //cell单元格
               "/[A-Z]+[0-9]+/g"
       };*/
        String[] patten = {
                //remoteCellRange表间单元格范围
                "[A-Za-z0-9_]+\\s*!\\s*[A-Za-z]+[0-9]+\\s*:\\s*[A-Za-z]+[0-9]+",
                //remoteCell表间单元格
                "[A-Za-z0-9_]+\\s*!\\s*[A-Za-z]+[0-9]+",
                //cellRange单元格范围
                "[A-Za-z]+[0-9]+\\s*:\\s*[A-Za-z]+[0-9]+",
                //cell单元格
                "[A-Z]+[0-9]+"
        };
        String formula = this.dataFormula;
        //todo:清空依赖关系
        //如果存在计算公式，开始扫描公式中的单元格
        if(!StringUtil.isEmptyOrNull(formula)){
            for(int a = 0;a < patten.length;a ++){
//                List<String> cellMatch = Utility.execJavascriptMatch(patten[a], formula);
                List<String> cellMatch = Utility.match(patten[a], formula,true);
                if(!StringUtil.isEmptyOrNull(cellMatch) && cellMatch.size() > 0){
                    String pat = patten[a].replaceAll("/|g", "");
                    formula = formula.replaceAll(pat,"");
                    switch (a){
                        case 0://remoteCellRange表间单元格范围

                            //todo
                            break;
                        case 1://remoteCell表间单元格
                            //todo
                            for(String remoteCell : cellMatch){
                                String[] formulaPart = remoteCell.split("!");
                                String sheetId = formulaPart[0];
                                String cellPart = formulaPart[1];
                                Cell remoteCell1 = this.sheet.getRemoteCell(sheetId, cellPart);
                                //todo:



                            }//for end
                            break;
                        case 2://cellRange单元格范围
                            for (String cellRange : cellMatch){
                                String[] cellPart = cellRange.split(":");
                                if(cellPart.length < 2){
                                    continue;
                                }
                                String cellStart = cellPart[0].replaceAll("\\s","");
                                String cellStop = cellPart[1].replaceAll("\\s","");
//                                this.sheet.getCellRange(cellStart,cellStop);

                                //todo:
                                    Map<String, Cell> cellRangeValue
                                            = this.sheet.getCellRange(cellStart,cellStop);
                                    for(Map.Entry<String,Cell> entry
                                            : cellRangeValue.entrySet()){
                                        Cell cell = entry.getValue();
                                        String address = entry.getKey();
                                        if(!this.dependencies.containsKey(address)
                                                && !StringUtil.isEmptyOrNull(cell)){
                                            this.dependencies.put(address,cell);
                                            cell.registerDependant(this.getAddress(),this);
                                        }//if end
                                    }//#for end
                            }//#for end
                            break;
                        case 3://cell单元格
                           for(String cellPart : cellMatch){
                               Cell cell = this.sheet.getCell(cellPart);
                               if(!this.dependencies.containsKey(cellPart)
                                       && !StringUtil.isEmptyOrNull(cell)){
                                   this.dependencies.put(cellPart,cell);
                                   cell.registerDependant(this.getAddress(),this);
                               }//#if end
                           }//#for end
                            break;
                    }//#switch end
                }//#if end
            }//#for end
        }//#if end
    }//cell.buildDependency() end

    /**
     * @Method      registerDependant
     * @Param		address
     * @Param		cell
     * @Return      void
     * @Exception
     * @Description [注册单元格依赖]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/19 17:38
     */
    private void registerDependant(String address, Cell cell) {
        if(!this.dependant.containsKey(address)
                && !StringUtil.isEmptyOrNull(cell)){
            this.dependant.put(address,cell);
        }
    }//registerDependant end

    /**
     * @Method      processDependency
     * @Param
     * @Return      void
     * @Exception
     * @Description [处理单元的依赖列表并将其标记为处理]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 10:10
     */
    public void processDependency() throws Exception {
//        System.out.println("sheet#[" + this.sheet.getIdentifier() + "]cell#[" + this.address + "] processing dependency");
//        logger.info("sheet#[" + this.sheet.getIdentifier() + "]cell#[" + this.address + "] processing dependency");
        if(!this.isProcessed()){
            /*System.out.println("sheet#[" + this.sheet.getIdentifier()
                    + "]cell#[" + this.address
                    + "] processing flag is ["
                    + this.processed + "], processing...");
            */
           /* logger.info("sheet#[" + this.sheet.getIdentifier()
                    + "]cell#[" + this.address
                    + "] processing flag is ["
                    + this.processed + "], processing...");*/
            if(!StringUtil.isEmptyOrNull(this.dependencies)
                    && this.dependencies.size() > 0){
                for(Map.Entry<String,Cell> entry
                        : this.dependencies.entrySet()){
                    Cell cell = entry.getValue();
                    cell.processDependency();

                }//for end
            }//if end
            this.evaluateFormula();
            this.setProcessed(true);
            if(!this.sheet.getAffectedCell().contains(this.getAddress())){
               this.sheet.getAffectedCell().add(this.address); 
            }//if end
        }//if end
    }//processDependency() end

    /**
     * @Method      evaluateFormula
     * @Param
     * @Return      void
     * @Exception
     * @Description [计算单元格公式，并将结果放入单元格容器中]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 10:48
     */
    public void evaluateFormula() throws Exception {
       /* System.out.println("sheet#[" + this.sheet.getIdentifier()
                + "]cell#[" + this.address
                + "] evaluating formula [" + this.dataFormula + "]");*/
      /* logger.info("sheet#[" + this.sheet.getIdentifier()
               + "]cell#[" + this.address
               + "] evaluating formula [" + this.dataFormula + "]");*/
        try {
            if(!StringUtil.isEmptyOrNull(this.dataFormula)){
                this.sheet.setActiveCell(this);
                this.computedValue = this.sheet.evaluate(this.dataFormula);
               /* System.out.println("sheet#[" + this.sheet.getIdentifier()
                        + "]cell#[" + this.address
                        + "] evaluating formula [" + this.dataFormula + "] result : " + this.computedValue);*/
              /* logger.info("sheet#[" + this.sheet.getIdentifier()
                       + "]cell#[" + this.address
                       + "] evaluating formula [" + this.dataFormula + "] result : " + this.computedValue);*/
            }//if end
        }catch (Exception e){
            throw e;
        }//try end
    }//cell.evaluateFormula()  end

    /**
     * @Method      renderComputedValue
     * @Param
     * @Return      void
     * @Exception
     * @Description [更新最终结果值]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 14:49
     */
    public void renderComputedValue() {
        //更新dataList值
      /*  System.out.println("cell.renderComputedValue() : sheet#[" + this.sheet.getIdentifier() + "]cell#["
        + this.getAddress() + "] result : " + this.getValue());*/
        /*System.out.println("cell#[" + this.getAddress() + "]fieldName#[" + this.fieldName +"]rowNo#[" + this.rowNo + "]");*/

        /*logger.info("cell.renderComputedValue() : sheet#[" + this.sheet.getIdentifier() + "]cell#["
                + this.getAddress() + "] result : " + this.getValue());*/

        for(Map<String,Object> map : this.sheet.getDataList()){
            String rowno = StringUtil.toString(map.get("ROWNO"));
            if(rowno.equals(this.rowNo)){
                map.put(this.fieldName,this.getValue());
                break;
            }//if end
        }//for end
    }//cell.renderComputedValue() end

    /**
     * @Method      buildChangeDependency
     * @Param
     * @Return      void
     * @Exception
     * @Description [获取单元格改变受影响单元格]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/4/20 17:42
     */
    public void buildChangeDependency() {
        if(!StringUtil.isEmptyOrNull(this.dependant)
                && this.dependant.size() > 0){
            for(Map.Entry<String,Cell> entry : this.dependant.entrySet()){
                String address = entry.getKey();
                Cell cell = entry.getValue();
                this.sheet.getCalx().getChangeCellAddress().add(cell.fieldName + "_" + cell.rowNo);
                if(!StringUtil.isEmptyOrNull(cell.dependant)
                        && cell.dependant.size() > 0){
                    cell.buildChangeDependency();
                }//if end
            }//for end
        }//if end
    }//buildChangeDependency end

    public String getDataCell() {
        return dataCell;
    }

    public void setDataCell(String dataCell) {
        this.dataCell = dataCell;
    }

    public String getDataFormula() {
        return dataFormula;
    }

    public void setDataFormula(String dataFormula) {
        this.dataFormula = dataFormula;
    }

    public String getDataFormat() {
        return dataFormat;
    }

    public void setDataFormat(String dataFormat) {
        this.dataFormat = dataFormat;
    }

    public Sheet getSheet() {
        return sheet;
    }

    public void setSheet(Sheet sheet) {
        this.sheet = sheet;
    }

    public String getValue() {
        if(!StringUtil.isEmptyOrNull(this.dataFormula)){//存在计算公式，返回计算公式计算的值
            return this.computedValue;
        }
        return value;
    }

    /**
     * @Method      checkCircularReference
     * @Param		address
     * @Return      boolean
     * @Exception
     * @Description [单元格公式循环依赖检查]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/2 10:41
     */
    public boolean checkCircularReference(String address) {
        boolean isCircular = false;
        if(StringUtil.isEmptyOrNull(address)){
            address = this.getAddress();
        }//if end
        if(!StringUtil.isEmptyOrNull(this.getDataFormula())){
            //只有设置公式的才检查
            if(!StringUtil.isEmptyOrNull(this.dependencies)
                    && this.dependencies.size() > 0){
                for(Map.Entry<String,Cell> entry
                        : this.dependencies.entrySet()){
                    Cell cell = entry.getValue();
                    if(address.equals(cell.getAddress())){//公式等于单元格本身
                        isCircular = true;
                    }//if end
                    if(isCircular){
                        return isCircular;
                    }//if end
                    isCircular = cell.checkCircularReference(address);
                    if(isCircular){
                        return isCircular;
                    }//if end
                }//for end
            }//if end

        }//if end

        return isCircular;
    }//checkCircularReference end

    /**
     * @Method      removeDependant
     * @Param		key
     * @Return      void
     * @Exception
     * @Description [移除此单元格影响的其他单元格依赖]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/7 15:04
     */
    public Cell removeDependant(String key){
        if(!StringUtil.isEmptyOrNull(this.dependant)
                && this.dependant.size() > 0){
            if(this.dependant.containsKey(key)){
                return this.dependant.remove(key);

            }//if end
        }//if end
        return null;
    }//removeDependant end

    /**
     * @Method      removeDependency
     * @Param		key
     * @Return      void
     * @Exception
     * @Description [移除影响此单元格的依赖单元格]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/7 15:06
     */
    public Cell removeDependency(String key){
       if(!StringUtil.isEmptyOrNull(this.dependencies)
               && this.dependencies.size() > 0){
           if(this.dependencies.containsKey(key)){
               return this.dependencies.remove(key);
           }//if end
       }//if end
        return null;
    }//removeDependency end

    /**
     * @Method      setFormula
     * @Param		formula
     * @Return      com.hongtu.tms.ledger.calx.factory.cell.Cell
     * @Exception
     * @Description [设置单元格计算公式并标记]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/7 15:15
     */
    public Cell setFormula(String formula){

        if(!StringUtil.isEmptyOrNull(formula)){
            this.dataFormula = formula;
            if(!this.sheet.getAffectedCell().contains(this.getAddress())){
                this.sheet.getAffectedCell().add(this.getAddress());
            }
        }
        return this;
    }//setFormula end

    /**
     * @Method      setValue
     * @Param		value
     * @Return      com.hongtu.tms.ledger.calx.factory.cell.Cell
     * @Exception
     * @Description [设置单元格值并标记]
     * @Author      zhuyongjie
     * @Version     1.0
     * @Date        2018/5/7 15:17
     */
    public Cell setValue(String value) {
        this.value = value;
        if(!this.sheet.getAffectedCell().contains(this.getAddress())){
            this.sheet.getAffectedCell().add(this.getAddress());
        }
        return this;
    }//setValue end

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public boolean isAffected() {
        return affected;
    }

    public void setAffected(boolean affected) {
        this.affected = affected;
    }

    public boolean isProcessed() {
        return processed;
    }

    public void setProcessed(boolean processed) {
        this.processed = processed;
    }

    public String getFloatValue() {
        return floatValue;
    }

}
